//////////////////////////////////////////////////////////////////////////
/// @file qaftrace.h
/// @brief Set of classes for printing trace logs to files.
//////////////////////////////////////////////////////////////////////////

#ifndef _QAFTRACE_H_
#define _QAFTRACE_H_

// There are two kinds of trace logs: DEBUG and RELEASE.
//    RELEASE logs are always allowed,
//    DEBUG logs are allowed only in DEBUG builds (when DEBUG or _DEBUG is defined).
// macros for DEBUG builds:
//    QD_TRACE( device, level, expr ) - DEBUG, allows specifying trace level
//    QD_TRACEX( log, expr )          - DEBUG, uses default trace level of the trace device
//    QD_TRACE_ERROR( log, expr )     - DEBUG, TraceError trace level
//    QD_TRACE_INFO( log, expr )      - DEBUG, TraceInfo trace level
//    QD_TRACE_DEBUG( log, expr )     - DEBUG, TraceDebug trace level
//    QD_TRACE_DETAIL( log, expr )    - DEBUG, TraceDetail trace level
//    Q_TRACEX( log, expr )           - for backward compatibility, same as QD_TRACE
// macros for RELEASE and DEBUG builds:
//    QR_TRACE( device, level, expr ) - DEBUG and RELEASE, allows specifying trace level
//    QR_TRACEX( log, expr )          - DEBUG and RELEASE, uses default trace level of the trace device
//    QR_TRACE_ERROR( log, expr )     - DEBUG and RELEASE, TraceError trace level
//    QR_TRACE_INFO( log, expr )      - DEBUG and RELEASE, TraceInfo trace level
//    QR_TRACE_DEBUG( log, expr )     - DEBUG and RELEASE, TraceDebug trace level
//    QR_TRACE_DETAIL( log, expr )    - DEBUG and RELEASE, TraceDetail trace level
// macros for regulating:
//    DISABLE_Q_TRACE - disable all RELEASE and DEBUG mode trace logs
//    ENABLE_Q_TRACE - enable all RELEASE and DEBUG mode trace logs (even in RELEASE builds)

#if defined(DISABLE_Q_TRACE) && defined(ENABLE_Q_TRACE)
	#error Only one of the following macros may be defined: DISABLE_Q_TRACE and ENABLE_Q_TRACE conflict.
#endif

/// This is an optimized directive for trace logs
/// When the trace logs are not enabled at runtime, this macro makes
/// the performance close to the original (without any trace logs).
#if (defined(ENABLE_Q_TRACE) || defined(_DEBUG) || defined(DEBUG)) && !defined(DISABLE_Q_TRACE)
	#define QD_TRACE( device, level, expr ) if( ! (device).IsEnabled( level ) ) int i = 0; else { QTrace::Record __tmpRec83745(__FILE__,__LINE__); (device) << __tmpRec83745 << expr; }
	// former does not work on Linux by some reason (bug/feature in gcc?)
	//#define QD_TRACE( device, level, expr ) if( ! (device).IsEnabled( level ) ) int i = 0; else (device) << QTrace::Record(__FILE__,__LINE__) << expr
	#define QD_TRACE_ENABLED
#else
	#define QD_TRACE( device, level, expr ) do { /* do nothing */ } while (false)
	#undef QD_TRACE_ENABLED
#endif

/// Output trace information with the default trace level of the device (DEBUG build only)
#define QD_TRACEX( device, expr ) QD_TRACE( (device), (device).GetDefaultTraceLevel(), expr )

/// Output trace information with QTrace::TraceError trace level (DEBUG build only)
#define QD_TRACE_ERROR( device, expr ) QD_TRACE( (device), QTrace::TraceError, expr )

/// Output trace information with QTrace::TraceInfo trace level (DEBUG build only)
#define QD_TRACE_INFO( device, expr ) QD_TRACE( (device), QTrace::TraceInfo, expr )

/// Output trace information with QTrace::TraceDebug trace level (DEBUG build only)
#define QD_TRACE_DEBUG( device, expr ) QD_TRACE( (device), QTrace::TraceDebug, expr )

/// Output trace information with QTrace::TraceDetail trace level (DEBUG build only)
#define QD_TRACE_DETAIL( device, expr ) QD_TRACE( (device), QTrace::TraceDetail, expr )

/// Output trace information with QTrace::TraceError trace level (DEBUG build only).
/// This macro is left for backward compatibility.
#define Q_TRACEX( device, expr ) QD_TRACEX( (device), expr )

/// This is an optimized directive for trace logs
/// When the trace logs are not enabled at runtime, this macro makes
/// the performance close to the original (without any trace logs).
#if !defined(DISABLE_Q_TRACE)
	#define QR_TRACE( device, level, expr ) if( ! (device).IsEnabled( level ) ) int i = 0; else { QTrace::Record __tmpRec83745(__FILE__,__LINE__); (device) << __tmpRec83745 << expr; }
	// former does not work on Linux by some reason (bug/feature in gcc?)
	//#define QR_TRACE( device, level, expr ) if( ! (device).IsEnabled( level ) ) int i = 0; else (device) << QTrace::Record(__FILE__,__LINE__) << expr
	#define QR_TRACE_ENABLED
#else
	#define QR_TRACE( device, level, expr ) do { /* do nothing */ } while (false)
	#undef QR_TRACE_ENABLED
#endif

/// Output trace information with the default trace level of the device (DEBUG and RELEASE builds)
#define QR_TRACEX( device, expr ) QR_TRACE( (device), (device).GetDefaultTraceLevel(), expr )

/// Output trace information with QTrace::TraceError trace level (DEBUG and RELEASE builds)
#define QR_TRACE_ERROR( device, expr ) QR_TRACE( (device), QTrace::TraceError, expr )

/// Output trace information with QTrace::TraceInfo trace level (DEBUG and RELEASE builds)
#define QR_TRACE_INFO( device, expr ) QR_TRACE( (device), QTrace::TraceInfo, expr )

/// Output trace information with QTrace::TraceDebug trace level (DEBUG and RELEASE builds)
#define QR_TRACE_DEBUG( device, expr ) QR_TRACE( (device), QTrace::TraceDebug, expr )

/// Output trace information with QTrace::TraceDetail trace level (DEBUG and RELEASE builds)
#define QR_TRACE_DETAIL( device, expr ) QR_TRACE( (device), QTrace::TraceDetail, expr )

/// This file will be compiled only if DISABLE_Q_TRACE is not defined!!!
#if !defined(DISABLE_Q_TRACE)
/// This file will be compiled only if DISABLE_Q_TRACE is not defined!!!

#ifdef WIN32
	#include <windows.h>
	#include <tchar.h>
#endif

#include <stdio.h>
#include <sstream>

#include "csynccs.h"
#include "qafdebug.h"
#include "registryutils.h"

/////////////////////////////////////////////////////////////////////////////////////////////
// Namespace for all trace classes (main clases are
//     QTrace::LogFile, QTrace::Log and QTrace::Dump
namespace QTrace
{
/////////////////////////////////////////////////////////////////////////////////////////////

/// Levels of tracing
enum TraceLevel
{
	TraceNone   =  0, ///< no trace
	TraceError  = 20, ///< only trace errors (textual description of errors)
	TraceInfo   = 40, ///< some extra error info (save input parameters that caused the error)
	TraceDebug  = 60, ///< debugging info (trace the main steps of the process)
	TraceDetail = 80, ///< detailed debugging info (trace the every single step of the process)
	TraceAll    = 100 ///< trace everything
};

///////////////////////////////////////////////////////////////////////////////////////////
// Format of the trace log
///////////////////////////////////////////////////////////////////////////////////////////
// time with ms pid  thread             location
// 15:05:49:547 [A5F:E67] Trace message <filename.cpp:120>
///////////////////////////////////////////////////////////////////////////////////////////
// Format of the memory dump
///////////////////////////////////////////////////////////////////////////////////////////
// time with ms pid  thread             location
// 15:05:49:547 [A5F:E67] Memory dump of "CType", address 0x120A, size 16b (0x10) <filename.cpp:120>
// 0000120A  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  1.2.3.4. 5.6.7.8.
///////////////////////////////////////////////////////////////////////////////////////////

/// Forward definition of the trace class
class Device;

/// Base class for all message construction classes
class Record
{
public:

	/// Enumeration for stages of the
	enum TraceStage
	{
		/// this should be a short trace message between prefix and postfix
		/// for most trace records this will be the only message they output
		TRACE_HEADER = 10,
		/// this should be a multiline trace message after the header line
		/// it does not include the prefix and postfix (memory dump or alike)
		TRACE_BODY = 20
	};

	/// Constructor receives the source code file name and line number
	Record( Q_LPCSTR szFileName, const int iLine )
		: m_szFileName(szFileName), m_iLine(iLine), m_pDevice(NULL),
		m_bTraced(false)
	{
	}

	/// Default constructor does nothing except initializing
	Record() : m_szFileName(NULL), m_iLine(0), m_pDevice(NULL),
		m_bTraced(false)
	{
	}

	/// Destructor does whatever it is needed to clear
	/// but before it completes the trace output if it was not performed yet
	virtual ~Record()
	{
		if( ! m_bTraced && Q_ASSERT( NULL != m_pDevice ) )
			DoTrace();
		m_szFileName = NULL;
		m_iLine = 0;
		m_pDevice = NULL;
		m_bTraced = true;
	}

	/// Set the current trace device
	void SetDevice( Device * device )
	{
		if( Q_ASSERT( NULL != device ) )
			m_pDevice = device;
	}

	/// Get the current trace device
	/// It may return NULL if the device was not yet initialized
	Device * GetDevice() const { return m_pDevice; }

	/// Output the current trace record to the trace device
	void DoTrace();

	/// Add new portion of text to the current trace record
	void AddText( const char * szText )
	{
		if( Q_ASSERT( NULL != szText ) )
			m_Buffer << szText;
	}

	/// Set location in source code
	void SetLocation( Q_LPCSTR szFileName, const int iLine )
	{
		if( Q_ASSERT( NULL != szFileName ) )
		{
			m_szFileName = szFileName;
			m_iLine = iLine;
		}
	}

	/// Get source code file name
	Q_LPCSTR GetLocationFile() const { return m_szFileName; }

	/// Get source code line
	int GetLocationLine() const { return m_iLine; }

	/// This function should indicate if this record does not contain
	/// an error message (it serves only as a container for source code location)
	virtual bool IsEmpty() const 
	{ 
		#ifdef WIN32
			int i1 = m_Buffer.rdbuf()->in_avail();
			int i2 = m_Buffer.gcount();
			int i3 = m_Buffer.str().length();
			return 0 == i3;
		#else
			//m_Buffer.freeze( false ); not sure in the role of this function
			return m_Buffer.eof();
		#endif
	}

protected:

	/// The pointer to the file name of source code
	/// This is a pointer to static string, never give
	/// a dynamic string pointers as the file location!
	Q_LPCSTR m_szFileName;

	/// Line number in source code
	int m_iLine;

	/// Pointer to the current device
	Device * m_pDevice;

	/// This flag is used to check in the destructor and trace
	/// the record if it was not yet traced.
	/// It must be assigned to true in DoTrace()
	bool m_bTraced;

	/// This is the buffer that is used for accumulating the text
	std::stringstream m_Buffer;

	/// This class should be a friend to access the DoLog() function
	friend class Device;

	/// Write count of bytes to the trace device (wrapper for the Device method)
	bool Write( const void * const pBuffer, const unsigned long dwBufferSize,
		unsigned long & dwBytesWrittenRet );

	/// This function will be called from the device's DoTrace() function
	/// The device opened the output stream already, and this function
	/// can write the record to it.
	virtual unsigned long TraceRecord( const TraceStage stage, bool & bFinishedRet );

	/// Assignment operator is disabled
	Record & operator=( Record & obj ) { Q_ASSERT( false ); }

	/// Copy constructor is disabled
	Record( Record & obj ) : m_szFileName(NULL), m_iLine(0), m_pDevice(NULL),
		m_bTraced(true) { Q_ASSERT( false ); }

private:
};

/// This class knows how to output a memory dump
class Dump: public Record
{
public:

	/// Constructor prepares or constructs the error message
	Dump( const void * const pBuf, const unsigned long ulBufSize,
		Q_LPCTSTR szTypeName = _T("void *") ) : m_pBuf(pBuf),
		m_ulBufSize(ulBufSize), m_szTypeName(szTypeName) {}

	/// Destructor does whatever it is needed to clear
	virtual ~Dump()
	{
		if( ! m_bTraced && Q_ASSERT( NULL != m_pDevice ) )
			DoTrace();
		m_pBuf = NULL;
		m_ulBufSize = 0;
		m_szTypeName = NULL;
	}

	/// This function should indicate if this record does not contain
	/// an error message (it serves only as a container for source code location)
	virtual bool IsEmpty() const { return false; }

protected:

	/// Pointer to the buffer to dump
	const void * m_pBuf;

	/// Size of the buffer to dump
	unsigned long m_ulBufSize;

	/// Pointer to the type name string
	Q_LPCTSTR m_szTypeName;

	/// This function will be called from the device's DoTrace() function
	/// The device opened the output stream already, and this function
	/// can write the record to it.
	virtual unsigned long TraceRecord( const TraceStage stage, bool & bFinishedRet );

	/// Assignment operator is disabled
	Dump & operator=( Dump & obj ) {}

	/// Copy constructor is disabled
	Dump( Dump & obj ) {}

private:
};

/// Trace class that takes care of the trace file and synchronization
class Device
{
public:

	/// szID serves as the id in the registry to switch the log on,
	/// and as the name of the mutex for synchronizing the access
	/// to the log device.
	/// bNeedMutex indicates if the mutex should be created for synchronization.
	/// nDefaultTraceLevel indicates which trace level will have the records
	/// by default. This trace level will be compared with the enabled trace level
	/// from the registry.
	/// The ID should not be NULL, otherwise the trace log will be switched off.
	Device( Q_LPCTSTR szID, const bool bNeedMutex,
		const TraceLevel nDefaultTraceLevel );

	/// Destructor
	virtual ~Device();

	/// Write the trace record to the output device, this function wraps 
	/// other virtual functions that have to be overloaded by descendant classes.
	void DoTrace( Record & obj );

	/// Check if the trace log is enabled for the given trace level.
	bool IsEnabled( const TraceLevel level ) const
	{
		return GetEnabledTraceLevel() >= (unsigned long)level;
	}

	/// Return the enabled trace level in the registry
	unsigned long GetEnabledTraceLevel() const
	{
		return m_dwEnabledTraceLevel;
	}

	/// Return the default trace level for the device
	TraceLevel GetDefaultTraceLevel() const
	{
		return m_nDefaultTraceLevel;
	}

protected:

	/// This class should have access to this class's Write() function
	friend class Record;

	/// In-process synchronization object for the device
	CAutoCS m_cs;

	/// This mutex, if requested by the descentant of this class
	/// will synchronize the access to the physical log device.
	HANDLE m_hMutex;

	/// The ID of the log device. This ID will be the name of
	/// the synchronization mutex and the value in the registry
	/// to switch the log on/off.
	Q_LPTSTR m_szID;

	/// The default trace level of the records in this log device.
	TraceLevel m_nDefaultTraceLevel;

	/// Wait timeout for the mutex object
	int m_dwWaitTimeout;

	/// Registry value that must be defined in order to get the trace log.
	/// By default all trace logs are disabled.
	/// The trace log may be enabled from the registry HKLM\Software\Company\Log\ + LOG_FILE_NAME
	/// By entering the value from 0 to 100.
	unsigned long m_dwEnabledTraceLevel;

	/// Write count of bytes to the log (this method is used by LogMessageBase descentants)
	virtual bool Write( const void * const pBuffer, const unsigned long dwBufferSize, 
		unsigned long & dwBytesWrittenRet ) = 0;

	/// Write the prefix for the header string (date time, etc)
	virtual unsigned long WritePrefix( Record & record, const unsigned long dwBytesWritten );

	/// Write the postfix for the header string (file and line)
	virtual unsigned long WritePostfix( Record & record, const unsigned long dwBytesWritten );

	/// Lock the trace device
	bool Lock();

	/// Unlock the trace device
	void Unlock();

	// close the current trace device
	virtual void CloseTraceDevice() {}

	// open the trace device
	virtual bool OpenTraceDevice() { return true; }

	/// Return the position of far right align that will be used
	/// for logging the source code file name and line
	/// This makes the trace log cleaner but still informative.
	/// Returning 0 or any small value will mean that the file name
	/// will be located right after the trace log message.
	virtual unsigned short GetFarRightColumn() { return 120; }

};

/// Output the trace information to the log file.
/// It is regulated by two registry values:
///     /HKLM/Software/Company/Log/
///          <log id>      : unsigned long = <enabled trace level>
///          <log id>.size : unsigned long = <maximum log file size>
class LogFile: public Device
{
public:

	/// Initialize the trace log file.
	/// szFileName must specify a file name without path.
	/// nMaxLogFileSize must specify the maximum size of the trace log
	/// (divided on 2 files) in bytes. 0 disables output to this log.
	LogFile( Q_LPCTSTR szFileName, const TraceLevel nDefaultTraceLevel,
		const int nMaxLogFileSize );

	/// Destructor
	virtual ~LogFile();

protected:

	/// Pointer to the log file
	FILE * m_hFile;

	/// File name of the log file, fully specified
	Q_TCHAR m_szFilename[Q_MAX_PATH];

	/// Maximum allowed log file size, configurable
	int m_nMaxLogFileSize;

	/// Write count of bytes to the log (this method is used by LogMessageBase descendants)
	virtual bool Write( const void * const pBuffer, const unsigned long dwBufferSize, 
		unsigned long & dwBytesWrittenRet );

	/// open a new trace file
	virtual bool OpenTraceDevice();

	/// close the current trace file
	virtual void CloseTraceDevice();

private:

};

/////////////////////////////////////////////////////////////////////////////////////////////
// CQWideCharToMultiByte class
/////////////////////////////////////////////////////////////////////////////////////////////

#ifdef WIN32

// convert the string to the UTF-8 format
class CQWideCharToMultiByte
{
public:

	// convert the string to the UTF-8 format
	CQWideCharToMultiByte( Q_LPCWSTR szInput, UINT uiCodePage = CP_UTF8 )
		: m_szBuffer(NULL), m_ulBufferSize(0)
	{
		if( Q_INVALID( NULL == szInput ) )
			return;
		int iStrLen = wcslen(szInput); // in wide chars
		if( 0 == iStrLen )
		{
			m_szBuffer = (Q_LPSTR) malloc( sizeof(char) );
			if( Q_ASSERT( NULL != m_szBuffer ) )
				m_szBuffer[0] = 0;
			return;
		}
		int iRet = WideCharToMultiByte( uiCodePage, 0, szInput, iStrLen, NULL, 0, NULL, NULL );
		if( Q_ASSERT( iRet > 0 ) )
		{
			m_szBuffer = (Q_LPSTR) malloc( sizeof(char) * (iRet + 1) );
			if( Q_ASSERT( NULL != m_szBuffer ) )
			{
				m_ulBufferSize = WideCharToMultiByte( uiCodePage, 0, szInput, iStrLen,
					m_szBuffer, iRet + 1, NULL, NULL );
				m_szBuffer[m_ulBufferSize] = 0;
				Q_ASSERT( iRet == m_ulBufferSize );
			}
		}
	}

	~CQWideCharToMultiByte()
	{
		if( NULL != m_szBuffer )
		{
			free( m_szBuffer );
			m_szBuffer = NULL;
		}
		m_ulBufferSize = 0;
	}

	/// Return the buffer (it may be UTF-8 which may be double-0-terminated!)
	Q_LPCSTR buffer() const
	{
		return m_szBuffer;
	}

	/// Return size of the string in bytes not including terminating 0 character
	unsigned long size() const
	{
		return m_ulBufferSize;
	}

	/// Return the pointer to the converted buffer and nullify it
	Q_LPCSTR Detach()
	{
		Q_LPCSTR szTemp = m_szBuffer;
		m_szBuffer = NULL;
		return szTemp;
	}

protected:

	/// The buffer - it is not a real 0-terminated string
	Q_LPSTR m_szBuffer;

	/// size of the string in bytes not including terminating 0 character
	unsigned long m_ulBufferSize;

private:
};

#endif

/////////////////////////////////////////////////////////////////////////////////////////////
// Helper formatting classes
/////////////////////////////////////////////////////////////////////////////////////////////

/// This is a printf-style formatting class. Use it with causion!
/// printf-style functions cannot detect ASCII and UNICODE strings and might fail!
/// This is also a base class for other formatting helper classes.
class Format
{
public:

	/// Be careful with this class since it cannot detect ASCII and UNICODE strings.
	/// It will assume it works with strings of TCHAR.
	Format( Q_LPCTSTR szFormat, ... );

	/// Destructor - destroys allocated memory
	virtual ~Format();

	/// Return the ASCII string with the formatted message
	virtual Q_LPCSTR GetTextA();

	#ifdef WIN32
		/// Return the WCHAR string with the formatted message
		virtual Q_LPCWSTR GetTextW();
	#endif

protected:

	/// Allocated string with the formatted message
	#if defined(_UNICODE) || !defined(WIN32)
		Q_LPSTR m_szConverted;
	#else
		Q_LPWSTR m_szConverted;
	#endif

	/// The buffer for the string
	Q_LPTSTR m_szBuffer;

	/// Constructor by default is purposed for descendant classes only
	Format() : m_szBuffer(NULL), m_szConverted(NULL) {}

private:
};

/// Helper class for rich formatting of integers
class Num: public Format
{
public:

	/// Since this class is only needed for rich formatting,
	/// you must specify the uiFixedLength. By default, the value
	/// will be right-aligned and prevailed with spaces.
	Num( const signed long lValue, const unsigned int uiFixedWidth,
		const Q_TCHAR bPadChar = ' ', const bool bLeftAlign = false );

protected:
private:
};

/// Helper class for rich formatting of integers in hex format
class Hex: public Format
{
public:

	/// By default, the hex value will not be padded with zeros,
	/// but you may change it. Align has no meaning here since
	Hex( const unsigned long ulValue, const unsigned int uiFixedWidth = 0,
		const Q_TCHAR bPadChar = '0', const bool bLeftAlign = false );

	/// By default, the hex value will not be padded with zeros,
	/// but you may change it. Align has no meaning here since
	Hex( const void * ptr, const unsigned int uiFixedWidth = 0,
		const Q_TCHAR bPadChar = '0', const bool bLeftAlign = false );

protected:

	/// Convert the given value or pointer to hexadecimal string.
	void Convert( const unsigned long ulValue, const unsigned int uiFixedWidth,
		const Q_TCHAR bPadChar, const bool bLeftAlign );

private:
};

/////////////////////////////////////////////////////////////////////////////////////////////
// overloaded operators <<
/////////////////////////////////////////////////////////////////////////////////////////////

/// Set new device and return the record
inline Record & operator <<( Device & device, Record & record )
{
	record.SetDevice( &device );
	return record;
}

/// Set new device and return the record
inline Record & operator <<( Record & record, Device & device )
{
	record.SetDevice( &device );
	return record;
}

/// Flush the current message and return the second message
inline Record & operator <<( Record & old_record, Record & new_record )
{
	old_record.DoTrace();
	new_record.SetDevice( old_record.GetDevice() );
	new_record.SetLocation( old_record.GetLocationFile(), old_record.GetLocationLine() );
	return new_record;
}

/// Add the formatted value to the current message and return the message
inline Record & operator <<( Record & record, Format & value )
{
	record.AddText( value.GetTextA() );
	return record;
}

/// Add a new part to the record
inline Record & operator <<( Record & record, Q_LPCSTR szText )
{
	record.AddText( szText );
	return record;
}

#ifdef WIN32
/// Add a new part to the record
inline Record & operator <<( Record & record, Q_LPCWSTR szText )
{
	CQWideCharToMultiByte conv( szText, CP_ACP );
	record.AddText( conv.buffer() );
	return record;
}
#endif

/// Add a new part to the record
inline Record & operator <<( Record & record, signed long lValue )
{
	const unsigned short BUF_LEN = 12;
	char szBuffer[BUF_LEN] = { 0 };
	#if defined(WIN32) && (_MSC_VER >= 1400)
		sprintf_s( szBuffer, BUF_LEN, "%d", lValue );
	#else
		sprintf( szBuffer, "%d", lValue );
	#endif
	record.AddText( szBuffer );
	return record;
}

/////////////////////////////////////////////////////////////////////////////////////////////
// Namespace for all trace classes (main clases are
//     QAFTrace::LogFile, QAFTrace::Log and QAFTrace::Dump
}; // namespace QAFTrace
/////////////////////////////////////////////////////////////////////////////////////////////

/// This file will be compiled only if DISABLE_Q_TRACE is not defined!!!
#endif // if !defined(DISABLE_Q_TRACE)
/// This file will be compiled only if DISABLE_Q_TRACE is not defined!!!

#endif // _QAFTRACE_H_
